在了解兩種控制器的差異,和接收Request的處理方式,今天來介紹ORM框架MyBatis的使用方式,結合資料庫撰寫的業務邏輯,可以讓網站應用程式實現更強大的功能,本篇重點會著重於MyBatis的基本配置和使用方式,最後在用簡單的API實現CRUD範例
Java ORM生態大致可分為兩種JPA及MyBatis兩種體系,前者的優勢在於對資料庫請求,有較完整的封裝,因此使用上可以減少SQL語句的撰寫,專注在模型地設計關聯資料庫即可,後者與JPA相反除了設定模型,必須自行撰寫SQL語句執行相關邏輯,在使用上必須學習MyBatis語句的使用方式,是要考量的學習成本
魚和熊掌不可兼得,要方便開發會受限於制式的使用工具,考量未來可能有特殊的情境,需要比較彈性的方式,實現SQL語句的撰寫,或是預設的SQL查詢不適用當前情境,此時MyBatis能細部調整SQL語句的特性,就是最好的選擇
在開始前,別忘了自行安裝資料庫(這邊以MySql為主),可以直接用IDE內建外掛安裝套件,或將下列XML內容貼至 pom.xml中
添加依賴項目pom.xml的內容
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
// 安裝 java mysql driver
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
Tips: mybatis-spring-boot-starter 版本,目前使用的Spring Boot版本必須支援,否則在編譯時會出現版本不相容問題,需手動設定Spring Boot支援的version,相關資訊可查看文件說明
兩個作法設定環境變數
第一個作法: 配置環境變數(Linux)
export DB_URL=jdbc:mysql://127.0.0.1:3306/{資料庫名稱}
export DB_USER=使用者
export DB_PASSWORD=使用者密碼
第二個作法: .env檔案內的變數載入環境變數中
執行 export $(xargs < .env)
配置應用程式設定 application.properties
#取得環境變數,套用至應用程式資料,用{}會從系統環境變數抓值
spring.datasource.url=${DB_URL}
spring.datasource.username=${DB_USER}
spring.datasource.password=${DB_PASSWORD}
mybatis.mapper-locations=classpath:mappers/*.xml
在資料庫建立 member 資料表DDL定義
CREATE TABLE `member` (
`id` BIGINT(19) NOT NULL AUTO_INCREMENT,
`account` VARCHAR(255) NOT NULL COLLATE 'utf8mb4_general_ci',
`password` VARCHAR(255) NOT NULL COLLATE 'utf8mb4_general_ci',
`name` VARCHAR(255) NOT NULL COLLATE 'utf8mb4_general_ci',
PRIMARY KEY (`id`) USING BTREE,
INDEX `id` (`id`) USING BTREE
)
COLLATE='utf8mb4_general_ci'
ENGINE=InnoDB;
重點筆記:
classpath預設路徑是src/main/resources或/src/test/resources,因此classpath:mappers/*.xml可以解讀成,從src/main/resources下的mappers資料夾,尋找XML配置文件
在來尋找 src/main/resources/mappers目錄(預設沒有mappers,需要自行建立),在裡面放MyBatis Mapper XML檔案,撰寫MyBatis綁定的DAO介面,與實際執行的SQL語句
// Member.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.app.dao.MemberDao" >
<select id="getById" resultType="User">
SELECT * FROM members WHERE id = #{id}
</select>
<insert
id="create" parameterType="com.app.dto.Member"
>
INSERT INTO members (account, password, name)
VALUES (#{account}, #{password}, #{name})
</insert>
<delete id="delete">
DELETE FROM members WHERE id = #{memberId}
</delete>
</mapper>
在控制器注入Dao實現建立操作
定義一個Member DTO物件用來接收客戶端資料
public class Member {
public int id;
public String account;
public String password;
public String name;
}
由於是在資料庫做查詢操作,這時候定義一個抽象DAO層用來規範資料庫行為
// Location: src/main/com/app/demo/dao/MemberDao.java
import org.apache.ibatis.annotations.Mapper;
import com.app.demo.dto.Member;
@Mapper
public interface MemberDao {
public Boolean create(String name, String account, String password);
public Member getById(int memberId);
}
在控制器注入DAO,執行資料庫操作
打開ApiController實現CRUD操作,在開始前別忘了先在控制器注入依賴的MemberDao
@Autowired
private MemberDao memberDao;
Create:建立新資料
@PostMapping("/member")
public Map createMember(
@RequestBody Member member) {
memberDao.create(member);
Map<String, Object> responseData = new HashMap<>();
responseData.put("name", member.name);
return responseData;
}
Read 取得會員資料
@GetMapping("/member/{memberId}")
public Map getMmeber(
@PathVariable("memberId") int memberId) {
Member member = memberDao.getById(memberId);
Map<String, Object> responseData = new HashMap<>();
System.out.printf("QeuryId: %d", memberId);
responseData.put("queryId", memberId);
responseData.put("data", member);
return responseData;
}
Update 更新會員資料
這裡有個變化,利用參數的方式,將資料套用到Mapper中
首先要在Dao的方法參數中添加@param Annotation
//--- MemberDao.java
public int update(
@Param("memberId") int memberId,
@Param("member") Member mebmer
);
接著調整 Mapper File新增更新段落,用法將佔位字符,套用實際資料的屬性或實際值
//--- Member.xml
<update id="update">
UPDATE members
SET
account = #{member.account}, password = #{member.password},
name = #{member.name}
WHERE id = #{memberId}
</update>
最後在控制器新增會員更新的路由規則
@PatchMapping("/member/{memberId}")
public Map updateMember(
@RequestBody Member member,
@PathVariable("memberId") int memberId) {
memberDao.update(memberId, member);
Map<String, Object> responseData = new HashMap<>();
Member modifyMember = memberDao.getById(memberId);
responseData.put("update_id", memberId);
responseData.put("data", modifyMember);
return responseData;
}
Delete 移除會員
Mapper已經定義了操作的SQL語句,只需要在DAO新增 delete方法,以及APIController建立移除的資源即可
//-- MemberDao.java
新增移除方法即可
public int delete(int memberId);
//-- ApiController.java
@DeleteMapping("/member/{memberId}")
public Map updateMember(
@PathVariable("memberId") int memberId) {
int deleteResult = memberDao.delete(memberId);
Map<String, Object> responseData = new HashMap<>();
responseData.put("delete_id", memberId);
responseData.put("status", deleteResult);
return responseData;
}
整個操作是將執行的SQL另外定義在Mapper XML中,並且沒有實作執行SQL語句的邏輯,只需要將DAO的抽象方法與Mapper綁定對應的語句,MyBatis會將SQL語句中的佔位字符,透過預處理的方式,將實際數值套用到執行的SQL中,接著在執行SQL語句,完成相關操作,因此抽象的DAO層,專注資料庫的操作邏輯,實現與其他業務邏輯的隔離